package net.ufrog.common.app;

import net.ufrog.common.CoreConfig;
import net.ufrog.common.Logger;
import net.ufrog.common.exception.NotSignException;
import net.ufrog.common.exception.ServiceException;
import net.ufrog.common.utils.Strings;
import org.apache.log4j.PropertyConfigurator;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 应用信息
 *
 * @author ultrafrog
 * @version 1.0, 2013-10-01
 * @since 1.0
 */
public abstract class App {

    private static ThreadLocal<App> current 	        = new ThreadLocal<>();
    private static Map<String, List<String>> mMessage   = new ConcurrentHashMap<>();
    private static Map<String, String> mConfig          = null;
    private static YamlWrapper yamlWrapper              = null;
    private static PropertiesWrapper propertiesWrapper  = null;
    private static Boolean hasYamlConf                  = null;

    /**
     * 读取用户
     *
     * @return 用户
     */
    public abstract AppUser getUser();

    /**
     * 设置用户
     *
     * @param user 用户
     * @return 用户
     */
    public abstract AppUser setUser(AppUser user);

    /**
     * 读取令牌
     *
     * @return 令牌
     */
    public abstract String getToken();

    /**
     * 更新令牌
     *
     * @return 令牌
     */
    public abstract String updateToken();

    /**
     * 读取位置
     *
     * @return 位置
     */
    public abstract Locale getLocale();

    /**
     * 读取验证信息
     *
     * @return 验证信息
     */
    public abstract Validation getValidation();

    /**
     * 读取国际化消息
     *
     * @param key 键值
     * @param args 参数
     * @return 国际化消息
     */
    public abstract String getMessage(String key, Object... args);

    /**
     * 读取国际化消息<br>
     * 可以随机读取多消息键值
     *
     * @param key 键值
     * @param args 参数
     * @return 国际化消息
     */
    public String msg(String key, Object... args) {
        String message = getMessage(key, args);
        if (Strings.equals("_multi", message)) {
            List<String> lMessage = getMultiMessages(key, args);
            if (lMessage.size() > 0) {
                Integer index = new Random().nextInt(lMessage.size() - 1);
                message = lMessage.get(index);
            }
        }
        return message;
    }

    /**
     * 读取多消息
     *
     * @param key 键值
     * @param args 参数
     * @return 消息内容
     */
    private List<String> getMultiMessages(String key, Object... args) {
        if (!mMessage.containsKey(key)) {
            String k = key + "." + 0;
            String msg = getMessage(k, args);
            List<String> lMulti = new ArrayList<>();
            for (Integer i = 1; !Strings.empty(msg) && !Strings.equals(msg, k); i++) {
                lMulti.add(msg);
                Logger.info("add message by key: %s, value: %s", key, msg);

                k = key + "." + i;
                msg = getMessage(k, args);
            }
            mMessage.put(key, lMulti);
        }
        return mMessage.get(key);
    }

    /**
     * 读取当前应用
     *
     * @return 当前线程应用
     */
    public static App current() {
        return current.get();
    }

    /**
     * 读取当前应用
     *
     * @param requiredType 请求的应用类型
     * @return 当前线程应用
     */
    public static <T extends App> T current(Class<T> requiredType) {
        return requiredType.cast(current());
    }

    /**
     * 设置并返回当前应用
     *
     * @param value 应用内容
     * @param <T> 应用泛型
     * @return 当前线程应用
     */
    public static <T extends App> T current(T value) {
        current.set(value);
        return value;
    }

    /**
     * 当前用户
     *
     * @return 当前用户
     */
    public static AppUser user() {
        AppUser user = current().getUser();
        if (user == null) throw new NotSignException();
        return user;
    }

    /**
     * 国际化消息
     *
     * @param key 键值
     * @param args 参数
     * @return 国际化消息
     */
    public static String message(String key, Object... args) {
        return current().getMessage(key, args);
    }

    /**
     * 读取配置封装
     *
     * @return 配置封装
     */
    public static YamlWrapper getYamlWrapper() {
        if (hasYamlConf == null) {
            try {
                yamlWrapper = YamlWrapper.create(CoreConfig.FILE_YAML);
                hasYamlConf = Boolean.TRUE;
                return yamlWrapper;
            } catch (Exception e) {
                Logger.warn("cannot init yaml config. " + e.getMessage());
                hasYamlConf = Boolean.FALSE;
                return null;
            }
        } else if (hasYamlConf) {
            return yamlWrapper;
        } else {
            return null;
        }
    }

    /**
     * 读取属性封装
     *
     * @return 属性封装
     */
    public static PropertiesWrapper getPropertiesWrapper() {
        if (propertiesWrapper == null) {
            try {
                propertiesWrapper = PropertiesWrapper.create(CoreConfig.FILE_CONF);
                return propertiesWrapper;
            } catch (Exception e) {
                Logger.warn("cannot init properties config. " + e.getMessage());
                return null;
            }
        } else {
            return propertiesWrapper;
        }
    }

    /**
     * 配置
     *
     * @param key 键值
     * @return 对应的配置内容
     */
    public static String config(String key) {
        if (mConfig == null) {
            mConfig = new ConcurrentHashMap<>();
            if (getYamlWrapper() != null) {
                getYamlWrapper().getMap(null).forEach((k, v) -> mConfig.put(k, String.valueOf(v)));
            } else if (getPropertiesWrapper() != null) {
                Properties properties = getPropertiesWrapper().getProperties();
                properties.stringPropertyNames().forEach(name -> mConfig.put(name, properties.getProperty(name)));
            } else {
                throw new ServiceException("cannot find config file. please check it!");
            }
            configLog4j();
        }
        return mConfig.getOrDefault(key, null);
    }

    /**
     * 配置
     *
     * @param key 键值
     * @param defaultValue 默认值
     * @return 对应的配置内容
     */
    public static String config(String key, String defaultValue) {
        return Strings.empty(config(key), defaultValue);
    }

    /**
     * 设置配置
     *
     * @param values 配置映射
     */
    public static void config(Map<String, String> values) {
        if (mConfig == null) config("_$");
        mConfig.putAll(values);
    }

    /**
     * 读取配置
     *
     * @param key 键值
     * @param separator 分隔符
     * @param needPrefix 是否需要前缀
     * @return 属性
     */
    public static Properties properties(String key, String separator, Boolean needPrefix) {
        if (getYamlWrapper() != null) {
            return getYamlWrapper().getProperties(key, separator, needPrefix);
        } else if (getPropertiesWrapper() != null) {
            return getPropertiesWrapper().getProperties(key, separator, needPrefix);
        } else {
            Logger.warn("cannot find config. return empty properties");
            return null;
        }
    }

    /**
     * 读取配置并转换成对象
     *
     * @param key 键值
     * @param requestType 请求类型
     * @param <T> 请求范型
     * @return 目标对象
     */
    public static <T> T bean(String key, Class<T> requestType) {
        if (getYamlWrapper() != null) {
            return getYamlWrapper().getBean(key, requestType);
        } else if (getPropertiesWrapper() != null) {
            return getPropertiesWrapper().getBean(key, requestType);
        } else {
            return null;
        }
    }

    /** 配置日志参数 */
    private static void configLog4j() {
        Properties properties;
        if (getYamlWrapper() != null) {
            properties = getYamlWrapper().getProperties("log4j", ".", Boolean.TRUE);
        } else if (getPropertiesWrapper() != null) {
            properties = getPropertiesWrapper().getProperties("log4j", ".", Boolean.TRUE);
        } else {
            properties = null;
        } if (properties != null && properties.size() > 0) {
            PropertyConfigurator.configure(properties);
            Logger.info("set log4j configuration");
        }
    }
}
